home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
The Guided Tour of Multimedia (Second Edition)
/
The Guided Tour of Multimedia (Second Edition).iso
/
trials
/
qtw111
/
inc
/
qtmacros.inc
< prev
next >
Wrap
Text File
|
1993-04-11
|
33KB
|
739 lines
; ---------------------------------------------------------------------
;
; QTMACROS.INC - QuickTime for Windows Helper Macros
;
; Version 1.1
;
; (c) 1988-1993 Apple Computer, Inc. All Rights Reserved.
;
; ---------------------------------------------------------------------
COMMENT @
Video dispatch codes
@
VDSP_SETBANK EQU 1
VDSP_SLIDEWINDOW EQU 2
VDSP_SAVECONTEXT EQU 3
VDSP_RESTCONTEXT EQU 4
VDSP_SETTARGET EQU 5
VDSP_IDENTIFY EQU 21
VDSP_VERSION EQU 22
VDSP_BANKTABLE EQU 23
VDSP_BITBLTTYPE EQU 24
VDSP_SCANWIDTH EQU 25
VDSP_TERMINATE EQU 86
COMMENT @
BitBlt types for hardware support
@
BBL_NONE EQU 0 ; unknown BitBlt type
BBL_MOVSD EQU 1 ; use MOVSD
BBL_DRVR EQU 2 ; use the driver's BitBlt
BBL_MOVPL16 EQU 3 ; 16-bit planar
COMMENT @
BMP types
These should match the BMP_TYPE enumerated values in QTCODEC.H
@
BMP_NONE EQU 0 ; unknown type
BMP_DIB EQU 1 ; DIB
BMP_MONO EQU 2 ; monochrome
BMP_PACKED_4 EQU 3 ; packed 4 bit, e.g., Fahrenheit
BMP_PLANAR_4 EQU 4 ; VGA or SVGA
BMP_INDEX_8 EQU 5 ; palettized driver
BMP_5_5_5 EQU 6 ; 32,768 colors
BMP_5_6_5 EQU 7 ; XGA, Intel order
BMP_PLANAR_16 EQU 8 ; two planes of one byte each
BMP_8_8_8_RGB EQU 9 ; true color RGB
BMP_MEMERR EQU 10 ; insufficient memory for buffers
BMP_8_8_8_BGR EQU 11 ; true color BGR
BMP_5_6_5_M EQU 12 ; XGA, Motorola order
COMMENT @
Macros AlignBP and AlignBPRet:
Align BP on a DWORD boundary
On 386DX and 486 CPUs, applications get better performance by accessing
DWORD operands on DWORD boundaries. Unaligned operands require an extra
bus cycle. Our tests on various QuickTime assembler functions shows
unaligned operands typically add a 10% peformance penalty to the entire
function. By being careful, we can ensure that WORD arguments and WORD
local variables occur in pairs, but we cannot guarantee the alignment of
BP, which is used to access these variables. The alignment of BP is
determined at run time. MSC 7.0 aligns the stack only on WORD
boundaries.
This macro ensures that BP is aligned on a DWORD boundary. It is
intended only for use with NEAR PROCs that use the PASCAL calling
sequence.
For a NEAR PROC the function arguments and local variables are separated
by two WORDs, the return address and the caller's BP. Thus, an aligned
BP optimizes accesses to both. For a FAR PROC the function arguments
and local variables are separated by three WORDs, since the return
address is two WORDs. In that case, we must define a dummy WORD as the
first local variable and misalign BP, so that the memory references will
be aligned.
When BP is not aligned, the macro aligns it, then moves the function
arguments, return address and saved BP accordingly. The PASCAL
restriction allows the macro to find the byte size of the arguments in
the RET instruction. The application must insert label "AlignBPRet"
immediately after one of its RET instructions. The application must
also define a dummy WORD as the last local variable.
The macro assumes the direction indicator is 0 (forward), that the
application already saved SI and DI (via USES SI DI on the PROC
statement), and that ES and CX can be destroyed.
Upon exit from the function, we must adjust the stack pointer if BP was
changed at entry. We accomplish this by storing in the extra word made
available at the top of the stack the caller's return address. At the
location in the stack that normally holds the caller's return address,
we place an address in this program. At the doctored return address, we
execute a near return.
Macro AlignBPExit uses a machine instruction for the near RET to prevent
the assembler from generating epilog code.
@
AlignBP MACRO
LOCAL AlreadyAligned
TEST BP, 2 ;; Already on DWORD boundary?
JE AlreadyAligned ;; Skip if already aligned
CMP BYTE PTR AlignBPRet - 3, 0C2H ;; Expected RET instruction?
JNE AlreadyAligned ;; Don't adjust if environment unknown
MOV CX, WORD PTR [AlignBPRet - 2];; Get byte size of parameter list
SHR CX, 1 ;; Convert byte count to word count
ADD CX, 2 ;; Account for return address and saved BP
SUB BP, 2 ;; Perform the alignment ourselves
;; Requires a word of slop after all local
;; variables
PUSH SS ;; Copy SS to ES
POP ES ;;
MOV DI, BP ;; Set target pointer
LEA SI, [DI+2] ;; Set source pointer
REP MOVSW [DI],SS:[SI] ;; Move the arguments down
MOV AX, [BP+2] ;; Get caller's return address
MOV ES:[DI], AX ;; Store at top of stack
MOV [BP+2], OFFSET AlignFixSP ;; Substitute our return address
AlreadyAligned:
ENDM
AlignBPExit MACRO
AlignBPRet: ;; Immediately follow the RETN immed
ALIGN 16 ;; Align for better performance
AlignFixSP:
RETN ;; Return to caller without epilog
ENDM
COMMENT @
Macro BSWAPX:
Exchange the bytes in a DWORD register.
On a 486, the BSWAP instruction uses 1 clock instead of the 6 or 7
clocks required for this macro. Unfortunately, there is no cheap
inline way at run time to distinguish between a 386 and 486.
@
BSWAPX MACRO reg:REQ
Root SUBSTR <reg>, 2, 1
Xchg8 CATSTR Root, <X>
ROL Xchg8, 8
ROL reg, 16
ROL Xchg8, 8
ENDM
COMMENT @
Macro to initialize dirty list, initialize bank bounds table, set GS:0
to bank bounds table, set ES:EDI to initial hardware target address,
set the bank for banked adapters.
@
SetHdwTarget MACRO
LGS DI, BankBds ;; Point to bank bounds table
MOV DI, GS:[DI] ;; Get residual block count of first bad row
MOV NextBadBlock, DI ;; Save the residual block count to match
MOV AX, WORD PTR OffScreen ;; Point to dirty list
REPEAT BPPT * Mag
ADD AX, BWIDTH ;; Allocate room for offscreen pixels
ENDM ;; of REPEAT BPPT * Mag
IF Mag EQ 2
ADD AX, WdBytes ;; Allocate room for second scan line in pair
ENDIF
INC AX ;; Round up to even offset for efficiency
AND AL, 0FEH ;;
ADD AX, 4 ;; Leave room for post-processing
MOV DirtyListFirst, AX ;;
MOV AX, DESTY ;; Get starting row
MUL WdBytes ;; Multiply by width of adapter scan line
REPEAT BPPT ;; Bytes per target pixel
ADD AX, DESTX ;; Add in destination column
ADC DX, 0 ;; Bump bank number if carry
ENDM ;; of REPEAT BPPT
PUSH DX ;; Push high order word
PUSH AX ;; Push low order word
PUSH VDSP_SETTARGET ;; Dispatch code for SetTarget
CALL [Hardware] ;; Set the initial bank, also ES:DI
ADD SP, 6 ;; Remove arguments from the stack
MOV Bank, AX ;; Save the returned bank
MOV SelVRAM, ES ;; Save VRAM selector
ENDM ;; of SetHdwTarget MACRO
COMMENT @
Bank change macros:
Used by functions that write directly to video adapter hardware.
The bank change macros assume that all bank changes are of size 1. This
is so, since the program moves at most 4 rows at a time and each bank holds
at least 40 rows.
@
DoBankUp MACRO
INC Bank ;; Go to next bank
PUSH Bank ;; Pass argument to function
PUSH VDSP_SETBANK ;; Dispatch code for SetBank
CALL [Hardware] ;; Set the new bank
ADD SP, 4 ;; Remove arguments from the stack
ENDM
CheckBankUp MACRO nbr:REQ ;; Check bank, jumping only when the bank
JC NewBankUp&nbr& ;; changes
NewBankUp&nbr&Ret:
ENDM
SetBankUp MACRO nbr:REQ ;; Effect a bank change
ALIGN 16
NewBankUp&nbr&:
DoBankUp
JMP NewBankUp&nbr&Ret;; Return to main code
ENDM
DoBankDn MACRO
DEC Bank ;; Go to previous bank
PUSH Bank ;; Pass argument to function
PUSH VDSP_SETBANK ;; Dispatch code for SetBank
CALL [Hardware] ;; Set the new bank
ADD SP, 4 ;; Remove arguments from the stack
ENDM
CheckBankDn MACRO nbr:REQ ;; Check bank, jumping only when the bank
JC NewBankDn&nbr& ;; changes
NewBankDn&nbr&Ret:
ENDM
SetBankDn MACRO nbr:REQ ;; Effect a bank change
ALIGN 16
NewBankDn&nbr&:
DoBankDn
JMP NewBankDn&nbr&Ret;; Return to main code
ENDM
SlideWindow MACRO
PUSH Bank ;; SlideWindow may adjust this
PUSH VDSP_SLIDEWINDOW ;; Dispatch code for SlideWindow
CALL [Hardware] ;; Call the function
ADD SP, 4 ;; Remove arguments from the stack
;; AX = bank, ES:DI may be changed
MOV Bank, AX ;; Bank number may have changed
ENDM
COMMENT @
Macros used with downward dithers from 24 bit sources
Get554 is used with 8 bit targets. The result is placed in BX, where it
can then be used as an index into a dither table.
Get555 is used with 15 bit (5-5-5) targets.
Get565 and Get565M are used with 16 bit (5-6-5) targets.
@
Get554 MACRO offst:REQ ;;
MOV BH, [ESI+offst] ;; Get R
SHR BH, 3 ;; Save 5 bits of R
MOV BL, [ESI+offst+1] ;; Get G
SHL EBX, 5 ;; Save 5 bits of G
MOV BL, [ESI+offst+2] ;; Get B
SHR EBX, 4 ;; Get 5-5-4
ENDM
Get555 MACRO offst:REQ
MOV AH, [ESI+offst] ;; Get R
SHR AH, 3 ;; Save 5 bits of R
MOV AL, [ESI+offst+1] ;; Get G
SHL EAX, 5 ;; Save 5 bits of G
MOV AL, [ESI+offst+2] ;; Get B
SHR EAX, 3 ;; Get 5-5-5
ENDM
Get565 MACRO offst:REQ
MOV AH, [ESI+offst] ;; Get R
SHR AH, 3 ;; Save 5 bits of R
MOV AL, [ESI+offst+1] ;; Get G
SHL EAX, 6 ;; Save 6 bits of G
MOV AL, [ESI+offst+2] ;; Get B
SHR EAX, 3 ;; Get 5-6-5
ENDM
Get565M MACRO offst:REQ
Get565 offst ;; Get 5-6-5 in Intel order
ROL AX, 8 ;; Put bytes in Motorola order
ENDM
COMMENT @
Utility macros
The macro assumes CX bytes will be moved from DS:SI to ES:DI, and that
DX is available.
@
@Equals MACRO p1:REQ, p2:REQ
EXITM %( @InStr(,p1,p2) * @InStr(,p2,p1))
ENDM ;; of @Equals MACRO
CopyBytes MACRO
MOV DX, CX ;; Save byte count
SHR CX, 2 ;; Byte count to DWORD count
REP MOVSD ;; Copy most of the bytes
MOV CX, DX ;; Restore byte count
AND CX, 3 ;; 0-3 bytes left
SHR CX, 1 ;; Check for 2 or more bytes left
REP MOVSW ;; Copy stray WORD, if any
ADC CX, CX ;; 0-1 bytes left
REP MOVSB ;; Copy stray byte, if any
ENDM ;; of CopyBytes MACRO
COMMENT @
Macros used with decompressors that write directly to video adapter
hardware.
@
GoOnScreenX MACRO
ALIGN 16
GoOnScreen:
PUSH CX ;; Save register
MOV CX, DirtyListNext ;; Offset of next dirty list entry
SUB CX, DirtyListFirst ;; 4 * number of dirty list entries
JLE NotDirty ;; Skip if dirty list is empty
PUSH BX ;; Save registers
PUSH DX ;;
PUSH SI ;;
PUSH DI ;;
PUSH DS ;;
PUSH ES ;;
SHR CX, 2 ;; Number of dirty list entries
MOV DX, BadStart ;; Onscreen offset of line start
NEG DX ;; Offset in offscreen buffer of bank split
;; Merge adjacent ranges
;; Split a range that splits a bank
MOV DS, WORD PTR OffScreen +2 ;; Offscreen buffer is the source
MOV SI, DirtyListFirst ;; Point to first dirty list entry
LEA DI, [SI-4] ;; Allow room for one split entry
LoopNewRange:
MOV AX, [SI] ;; Get start of range
MOV [DI], AX ;; Save start of range
LoopSameRange:
MOV BX, [SI+2] ;; Get end of range
ADD SI, 4 ;; Point to next source slot
DEC CX ;; One fewer source slot
JLE RangeDone ;; Skip if no more source slots
CMP BX, [SI] ;; Is the next range adjacent to this?
JB RangeDone ;; Skip if ranges are not adjacent
JMP LoopSameRange ;; Keep combining adjacent ranges
RangeDone:
CMP AX, DX ;; Might split occur in this range?
JGE NoSplit ;; Skip if split does not occur in this range
CMP BX, DX ;; Does split occur in this range?
JLE NoSplit ;; Skip if split does not occur in this range
MOV [DI+2], DX ;;
ADD DI, 4 ;; Point to next target slot
MOV [DI], DX ;; Start of new range
NoSplit:
MOV [DI+2], BX ;; Set right edge of range
ADD DI, 4 ;; Point to next target slot
TEST CX, CX ;; Any source slots left?
JG LoopNewRange ;; Loop while source slots remain
OR WORD PTR [DI], -1;; Terminate the list
;; Make offsets relative to prior entries
;; Convert ending offsets to byte sizes
LEA CX, [DI+4] ;; Compute number of list entries
SUB CX, DirtyListFirst ;; 4 * number of WORDs in the list
LEA BX, [DI-2] ;; Point to last WORD in list
SHR CX, 1 ;; 2 * number of WORDs in list
DEC CX ;; Don't process first WORD
LoopDiff:
MOV AX, [BX-2] ;; Get previous WORD in list
SUB [BX], AX ;; Compute difference from previous WORD
SUB BX, 2 ;; Back up one WORD
DEC CX ;; One fewer WORD
JG LoopDiff ;; Loop once for each WORD in list
;; Copy the first line of the pair
MOV ES, SelVRAM ;; VRAM is the target
MOV DI, BadStart ;;
MOV SI, WORD PTR OffScreen ;; Offscreen buffer is the source
LoopCopy:
MOV AX, [BX] ;; Get offset of range start
TEST AX, AX ;; End of the list?
JL DirtyDone ;; Skip if end of the list
MOV CX, [BX+2] ;; Get number of bytes in the range
ADD BX, 4 ;; Point to next list entry
ADD SI, AX ;; Locate data in offscreen buffer
ADD DI, AX ;; Position pointer in onscreen buffer
CheckBankUp 01E ;; Change bank if necessary
CopyBytes ;; Copy the bytes
TEST DI, DI ;; Did range end on a bank boundary?
JZ NewBankUp02E ;; Skip if range ended on a bank boundary
NewBankUp02ERet:
JMP LoopCopy ;; Loop once for each range
ALIGN 16
DirtyDone:
TEST DI, DI ;; Did we cross the bank boundary?
JGE DirtyCrossed ;; Skip if we already crossed
DoBankUp ;; Cross the bank boundary
DirtyCrossed:
POP ES ;; Restore registers
POP DS ;;
POP DI ;;
POP SI ;;
POP DX ;;
POP BX ;;
POP CX ;;
RETN ;; Return to caller without epilog
ALIGN 16
NotDirty:
POP CX ;; Restore register
DoBankUp ;; Go to next bank
RETN ;; Return to caller without epilog
ENDM ;; of GoOnScreenX
GoOnScreen2x MACRO
ALIGN 16
GoOnScreen:
PUSH CX ;; Save register
MOV CX, DirtyListNext ;; Offset of next dirty list entry
SUB CX, DirtyListFirst ;; 4 * number of dirty list entries
JLE NotDirty ;; Skip if dirty list is empty
PUSH BX ;; Save registers
PUSH DX ;;
PUSH SI ;;
PUSH DI ;;
PUSH DS ;;
PUSH ES ;;
SHR CX, 2 ;; Number of dirty list entries
MOV BX, BadStart ;; Onscreen offset of line start
MOV DX, BX ;; Copy onscreen offset of line start
NEG DX ;; Offset in offscreen buffer of bank split?
ADD BX, WdBytes ;; Onscreen offset of second line in pair
JC FirstLineSplit ;; Skip if first line in pair has bank split
MOV DX, BX ;; Copy onscreen offset of line start
NEG DX ;; Offset in offscreen buffer of bank split
FirstLineSplit:
;; Merge adjacent ranges
;; Split a range that splits a bank
MOV DS, WORD PTR OffScreen +2 ;; Offscreen buffer is the source
MOV SI, DirtyListFirst ;; Point to first dirty list entry
LEA DI, [SI-4] ;; Allow room for one split entry
LoopNewRange:
MOV AX, [SI] ;; Get start of range
MOV [DI], AX ;; Save start of range
LoopSameRange:
MOV BX, [SI+2] ;; Get end of range
ADD SI, 4 ;; Point to next source slot
DEC CX ;; One fewer source slot
JLE RangeDone ;; Skip if no more source slots
CMP BX, [SI] ;; Is the next range adjacent to this?
JB RangeDone ;; Skip if ranges are not adjacent
JMP LoopSameRange ;; Keep combining adjacent ranges
RangeDone:
CMP AX, DX ;; Might split occur in this range?
JGE NoSplit ;; Skip if split does not occur in this range
CMP BX, DX ;; Does split occur in this range?
JLE NoSplit ;; Skip if split does not occur in this range
MOV [DI+2], DX ;;
ADD DI, 4 ;; Point to next target slot
MOV [DI], DX ;; Start of new range
NoSplit:
MOV [DI+2], BX ;; Set right edge of range
ADD DI, 4 ;; Point to next target slot
TEST CX, CX ;; Any source slots left?
JG LoopNewRange ;; Loop while source slots remain
;;
OR WORD PTR [DI], -1;; Terminate the list
;; Make offsets relative to prior entries
;; Convert ending offsets to byte sizes
LEA CX, [DI+4] ;; Compute number of list entries
SUB CX, DirtyListFirst ;; 4 * number of WORDs in the list
LEA BX, [DI-2] ;; Point to last WORD in list
SHR CX, 1 ;; 2 * number of WORDs in list
DEC CX ;; Don't process first WORD
LoopDiff:
MOV AX, [BX-2] ;; Get previous WORD in list
SUB [BX], AX ;; Compute difference from previous WORD
SUB BX, 2 ;; Back up one WORD
DEC CX ;; One fewer WORD
JG LoopDiff ;; Loop once for each WORD in list
;; Copy the first line of the pair
MOV ES, SelVRAM ;; VRAM is the target
MOV DI, BadStart ;;
MOV SI, WORD PTR OffScreen ;; Offscreen buffer is the source
LoopCopy1:
MOV AX, [BX] ;; Get offset of range start
TEST AX, AX ;; End of the list?
JL DirtyDone1 ;; Skip if end of the list
MOV CX, [BX+2] ;; Get number of bytes in the range
ADD BX, 4 ;; Point to next list entry
ADD SI, AX ;; Locate data in offscreen buffer
ADD DI, AX ;; Position pointer in onscreen buffer
CheckBankUp 01E ;; Change bank if necessary
CopyBytes ;; Copy the bytes
TEST DI, DI ;; Did range end on a bank boundary?
JZ NewBankUp02E ;; Skip if range ended on a bank boundary
NewBankUp02ERet:
JMP LoopCopy1 ;; Loop once for each range
ALIGN 16
;; Copy the second line of the pair
DirtyDone1:
MOV AX, BadStart ;; Onscreen offset of first line in pair
ADD AX, WdBytes ;; Onscreen offset of second line in pair
SUB AX, DI ;; Offset from current target pointer
ADD DI, AX ;; Add the offset back in
CheckBankUp 03E ;; Change bank if necessary
MOV SI, WORD PTR OffScreen ;; Offscreen buffer is the source
MOV BX, DirtyListFirst ;; Point to first dirty list entry
SUB BX, 4 ;; Post-processed first entry
LoopCopy2:
MOV AX, [BX] ;; Get offset of range start
TEST AX, AX ;; End of the list?
JL DirtyDone2 ;; Skip if end of the list
MOV CX, [BX+2] ;; Get number of bytes in the range
ADD BX, 4 ;; Point to next list entry
ADD SI, AX ;; Locate data in offscreen buffer
ADD DI, AX ;; Position pointer in onscreen buffer
CheckBankUp 04E ;; Change bank if necessary
CopyBytes ;; Copy the bytes
TEST DI, DI ;; Did range end on a bank boundary?
JZ NewBankUp05E ;; Skip if range ended on a bank boundary
NewBankUp05ERet:
JMP LoopCopy2 ;; Loop once for each range
ALIGN 16
DirtyDone2:
TEST DI, DI ;; Did we cross the bank boundary?
JGE DirtyCrossed2 ;; Skip if we already crossed
DoBankUp ;; Cross the bank boundary
DirtyCrossed2:
POP ES ;; Restore registers
POP DS ;;
POP DI ;;
POP SI ;;
POP DX ;;
POP BX ;;
POP CX ;;
RETN ;; Return to caller without epilog
ALIGN 16
NotDirty:
POP CX ;; Restore register
DoBankUp ;; Go to next bank
RETN ;; Return to caller without epilog
ENDM ;; of GoOnScreen2x
COMMENT @
Macro used to move pixels from onscreen buffer to offscreen buffer.
@
GoOffScreenX MACRO
LOCAL NewBank
LOCAL AllFits
LOCAL ExitPath
ALIGN 16
GoOffScreen:
PUSH DS ;; Save register
PUSH CX ;; Save register
PUSH DX ;; Save register
PUSH SI ;; Save register
PUSH DI ;; Save register
MOV CX, AX ;; Get byte count
MOV DS, SelVRAM ;; Point to onscreen buffer
MOV SI, DI ;; Copy target offset
SUB SI, WORD PTR OffScreen ;; Compute offset from start of buffer
ADD SI, BadStart ;; Index into onscreen buffer
JC NewBank ;; Skip if we crossed a bank boundary
ADD SI, CX ;; Check ending source offset
JNC AllFits ;; Skip if no bank change required
JZ AllFits ;; Skip if no bank change required
PUSH SI ;; Save byte count for new bank
SUB SI, CX ;; Restore source pointer
MOV CX, SI ;; Compute byte count for old bank
NEG CX ;;
CopyBytes ;; Copy the bytes
POP CX ;; Get byte count for new bank
NewBank:
DoBankUp ;; Go to new bank
CopyBytes ;; Copy the bytes
DoBankDn ;; Back to old bank
ExitPath:
POP DI ;; Restore register
POP SI ;; Restore register
POP DX ;; Restore register
POP CX ;; Restore register
POP DS ;; Restore register
RETN ;; Return to caller without epilog
ALIGN 16
AllFits: ;; Source does not cross a bank boundary
SUB SI, CX ;; Restore source pointer
CopyBytes ;; Copy the bytes
JMP ExitPath ;; Go to common exit
ENDM
COMMENT @
Macro to compute number of source bytes processed, used for BMP banding
@
BytesUsed MACRO
XOR EAX, EAX ;; Zero out high order word
MOV AX, WORD PTR Inbuf ;; Get offset of source pointer
SUB ESI, EAX ;; Compute number of source bytes processed
SHLD EDX, ESI, 16 ;; Put high order word in DX
MOV AX, SI ;; Put low order word in AX
ENDM
COMMENT @
Macros for moving pixels to and from overscan area
Note that the 2X versions of the macros should not be used with 8-bit
targets since dithering will produce different target values for the
same source.
We generate error messages only in the SavePixels macro, figuring they
would be superfluous in RestPixels.
@
SaveOneRow MACRO
j = 0
REPEAT Limit
MOV EAX, ES:[DI+j] ;; Get 4 bytes
MOV OverScan[i], EAX ;; Save the bytes
i = i + 4
j = j + 4
ENDM ;; of REPEAT Limit
ENDM
RestOneRow MACRO Width:REQ
j = 0
WHILE j LT Width - Width MOD 4
MOV EAX, OverScan[i] ;; Get 4 bytes
MOV ES:[DI+j], EAX ;; Restore the bytes
i = i + 4
j = j + 4
ENDM ;; of WHILE j LT Width - Width MOD 4
IF j LT Width - Width MOD 2
MOV AX, WORD PTR OverScan[i] ;; Get 2 bytes
MOV ES:[DI+j], AX ;; Restore the bytes
i = i + 2
j = j + 2
ENDIF ;; of IF j LT Width - Width MOD 2
IF j LT Width
MOV AL, BYTE PTR OverScan[i] ;; Get 1 byte
MOV ES:[DI+j], AL ;; Restore the byte
i = i + 1
j = j + 1
ENDIF ;; of IF j LT Width
i = (i + 3) AND 0FFFCH ;; Round up to multiple of 4
ENDM ;; of RestOneRow MACRO
SavePixels MACRO Width:REQ, Height:REQ, Increment:REQ
i = 0
Limit = (((Width) + 3) AND 0FFFCH) / 4
ReqdSize TEXTEQU %(Limit * Height)
IFNDEF OverScan
% .ERR <Define OverScan[ReqdSize]:DWORD>
EXITM
ENDIF
IF ( TYPE OverScan NE 4) OR (ReqdSize NE LENGTHOF OverScan)
% .ERR <Define OverScan[ReqdSize]:DWORD>
EXITM
ENDIF
REPEAT Height
ADD DI, Increment
SaveOneRow
ENDM ;; of REPEAT Height
ENDM ;; of MACRO
RestPixels MACRO Width:REQ, Height:REQ, Increment:REQ
i = 0
Limit = (((Width) + 3) AND 0FFFCH) / 4
ReqdSize TEXTEQU %(Limit * Height)
IFNDEF OverScan ;; If variable not defined
EXITM ;; Avoid further error messages
ENDIF
IF ( TYPE OverScan NE 4) OR (ReqdSize NE LENGTHOF OverScan)
EXITM
ENDIF
REPEAT Height
ADD DI, Increment
RestOneRow (Width)
ENDM ;; of REPEAT Height
ENDM ;; of MACRO
SavePix2X MACRO Width:REQ, Height:REQ, Increment:REQ
i = 0
Limit = (((Width) * 2 + 3) AND 0FFFCH) / 4
ReqdSize TEXTEQU %(Limit * Height)
IFNDEF OverScan
% .ERR <Define OverScan[ReqdSize]:DWORD>
EXITM
ENDIF
IF ( TYPE OverScan NE 4) OR (ReqdSize NE LENGTHOF OverScan)
% .ERR <Define OverScan[ReqdSize]:DWORD>
EXITM
ENDIF
REPEAT Height
ADD DI, Increment
ADD DI, Increment
SaveOneRow
ENDM ;; of REPEAT Height
ENDM ;; of MACRO
RestPix2X MACRO Width:REQ, Height:REQ, Increment:REQ
i = 0
Limit = (((Width) * 2 + 3) AND 0FFFCH) / 4
ReqdSize TEXTEQU %(Limit * Height)
IFNDEF OverScan ;; If variable not defined
EXITM ;; Avoid further error messages
ENDIF
IF ( TYPE OverScan NE 4) OR (ReqdSize NE LENGTHOF OverScan)
EXITM
ENDIF
PUSH BX ;; Save register
MOV BX, Increment ;; Initialize index register
j = 0
REPEAT Limit ;; Restore row #2 from row #1
MOV EAX, ES:[DI+j] ;; Get 4 uncorrupted bytes
MOV ES:[DI+BX+j], EAX;; Restore the bytes
j = j + 4
ENDM ;; of REPEAT Limit
REPEAT Height - 1
ADD DI, BX
ADD DI, BX
j = 0
REPEAT Limit
MOV EAX, OverScan[i] ;; Get 4 saved bytes
MOV ES:[DI+j], EAX ;; Restore the bytes to row #1
MOV ES:[DI+BX+j], EAX;; Restore the bytes to row #2
i = i + 4
j = j + 4
ENDM ;; of REPEAT Limit
ENDM ;; of REPEAT Height
ADD DI, BX ;; Point to last row
ADD DI, BX
RestOneRow ((Width) * 2)
POP BX ;; Restore register
ENDM ;; of MACRO